package com.dycloud.project.service;


import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import com.dycloud.project.constant.Constants;
import com.dycloud.project.domain.SysUser;
import com.dycloud.project.dto.LoginUserDTO;
import com.dycloud.project.exception.ServiceException;
import com.dycloud.project.exception.user.UserPasswordNotMatchException;
import com.dycloud.project.manager.AsyncManager;
import com.dycloud.project.manager.factory.AsyncFactory;
import com.dycloud.project.request.LoginRequest;
import com.dycloud.project.security.LoginUser;
import com.dycloud.project.security.context.AuthenticationContextHolder;
import com.dycloud.project.security.service.TokenService;
import com.dycloud.project.utils.MessageUtils;
import com.dycloud.project.utils.ip.IpUtils;
import com.dycloud.project.vo.LoginUserVo;
import com.dycloud.project.web.domain.AjaxResult;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;

/**
 * @author : renhang
 * @description UserLoginService
 * @date : 2025-07-02
 **/
@Slf4j
@Service
public class UserLoginService {
    
    @Resource
    private AuthenticationManager authenticationManager;
    
    @Resource
    private ISysUserService sysUserService;
    
    @Resource
    private TokenService tokenService;
    
    @Resource
    private WxMaService wxMaService;
    
    
    public AjaxResult userCreatToken(LoginRequest request) {
        try {

            // 1. 用code换取openid
            WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(request.getCode());
            String openId = session.getOpenid();
            
            LoginUserDTO userDTO = BeanUtil.copyProperties(request, LoginUserDTO.class);
            // 2. 查询或创建用户
            SysUser sysUser = sysUserService.queryByOpenId(openId);
            if (Objects.isNull(sysUser)) {
                userDTO.setOpenId(openId);
                // 初始化c端用户userName=随机生成 password=123456
                sysUser = sysUserService.createUser(userDTO);
            } else {
                if (!Objects.equals(sysUser.getDelFlag(), Constants.SUCCESS) || !Objects.equals(sysUser.getStatus(), Constants.SUCCESS)) {
                    log.warn("Current User Is Not User, userId is {}", sysUser.getUserId());
                    throw new ServiceException("无法登录，您已被禁用");
                }
                
                SysUser updateUser = new SysUser();
                updateUser.setUserId(sysUser.getUserId());
                updateUser.setLoginDate(new Date());
                updateUser.setLoginIp(IpUtils.getIpAddr());
                sysUserService.updateUserByUid(updateUser);
            }
            
            // 用户验证
            Authentication authentication = null;
            try {
                // username:userName  password: openId
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUserName(), sysUser.getUserName());
                AuthenticationContextHolder.setContext(authenticationToken);
                // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
                authentication = authenticationManager.authenticate(authenticationToken);
                
                // 这里security6必须手动设置，否则在aop中无法获取用户信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (Exception e) {
                if (e instanceof BadCredentialsException) {
                    recordLoginInfo(sysUser.getUserName(), Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"));
                    throw new UserPasswordNotMatchException();
                } else {
                    recordLoginInfo(sysUser.getUserName(), Constants.LOGIN_FAIL, e.getMessage());
                    throw new ServiceException(e.getMessage());
                }
            } finally {
                AuthenticationContextHolder.clearContext();
            }
            recordLoginInfo(sysUser.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            
            return AjaxResult.success(LoginUserVo.builder().openId(openId).userName(sysUser.getUserName()).token(tokenService.createToken(loginUser)).build());
        } catch (WxErrorException e) {
            return AjaxResult.error("微信登录失败: " + e.getMessage());
        }
        
    }
    
    /**
     * 记录登录信息，可以被子类重写以支持测试
     */
    protected void recordLoginInfo(String username, String status, String message) {
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, status, message));
    }
    

}
